--长连接管理
local filename = "long_tcp_mng.lua"

local incrservicepoolmng = require "incrservicepoolmng"
local filelog = require "filelog"
local skynet = require "skynet"
local timetool = require "timetool"

local service = nil

local LongTcpMng = {
	agentpool = nil,
	connections={},
	agents={},
	agentnum = 0,
	connectnum = 0, --连接数
}

--[[
connections 结构
fd =>{
		-- 前三个是初始就有的
		fd = fd,
		ipport = ipport,
		time = timetool.get_time(),
		-- 后两个是绑定到agent上才赋值的
		isclose = nil, -- cerate_session 后 isclose = flase, 心跳超时的时候，才唯一设置成 true
		rid = nil, -- 可以通过这个rid，去找到具体的agent
}

agents 结构
rid => {
	agent = agentservice.service
	fd = fd
	serviceid = agentservice.id	 -- 感觉这个没意义,去掉了
}
]]

local function unset_connections(fd)
	LongTcpMng.connections[fd] = nil
	LongTcpMng.connectnum = LongTcpMng.connectnum - 1
	if LongTcpMng.connectnum < 0 then
		LongTcpMng.connectnum = 0
	end
end

local function close_socket(fd)
	print(filename .. " line38, function close_socket")
	unset_connections(fd)
	skynet.pcall(skynet.call, service.gate_service, "lua", "kick", fd)
end

local function close_agent(fd)
	local c = LongTcpMng.connections[fd]
	local a
	if c ~= nil and c.rid ~= nil then
		unset_connections(fd)
		-- isclose 只有 nil, false ,true ,所以完全可以简化
		-- if c.isclose == nil or not c.isclose then 
		if  not c.isclose then 
			skynet.pcall(skynet.call, service.gate_service, "lua", "kick", fd)
		end
		a = LongTcpMng.agents[c.rid]
		if a ~= nil and a.fd == fd and a.agent ~= nil then
			LongTcpMng.agents[c.rid] = nil
			LongTcpMng.agentnum = LongTcpMng.agentnum - 1
			skynet.pcall(skynet.send, a.agent, "lua", "cmd", "close", fd)
		end
		return
	end 

	close_socket(fd)
end

function LongTcpMng.init(callservice, agentmodule, agentsize, agentincr, netpackmodule)
 	LongTcpMng.agentpool = incrservicepoolmng:new({}, {service_name=agentmodule, service_size=agentsize, incr=agentincr}, netpackmodule)
	service = callservice	
	skynet.fork(function()
		local now_time
		while true do
			skynet.sleep(400)
			now_time = timetool.get_time()
			for fd, c in pairs(LongTcpMng.connections) do
				if c.rid == nil then
					if c.time+20 <= now_time then
						filelog.sys_info("line77 LongTcpMng.init timeout kick")
						skynet.pcall(skynet.call, service.gate_service, "lua", "kick", fd)
						c.isclose = true
						filelog.sys_warning("line80 delete zombie connection", fd, c)
					end
				end
			end
		end
	end)

end

function LongTcpMng.agentexit(fd, rid)
	local c = LongTcpMng.connections[fd]
	local a = LongTcpMng.agents[rid]

	if c ~= nil and c.rid == rid then
		unset_connections(fd)
		if not c.isclose then
			skynet.pcall(skynet.call, service.gate_service, "lua", "kick", fd)
		end
		a = LongTcpMng.agents[rid]
		if a ~= nil and a.fd == fd then
			if a.agent ~= nil then
				skynet.pcall(skynet.send, a.agent, "lua", "cmd", "close", fd)
			end
			LongTcpMng.agents[rid] = nil
			LongTcpMng.agentnum = LongTcpMng.agentnum - 1
		--[[elseif a ~= nil and a.fd ~= fd then
			skynet.pcall(skynet.send, a.agent, "lua", "cmd", "close")
			LongTcpMng.agents[rid] = nil
			LongTcpMng.agentnum = LongTcpMng.agentnum - 1
			close_socket(a.fd)]]			
		end
		return
	end

	if rid ~= nil then
		a = LongTcpMng.agents[rid]
		if a ~= nil and a.fd == fd then
			if a.agent ~= nil then
				skynet.pcall(skynet.send, a.agent, "lua", "cmd", "close", a.fd)
			end
			LongTcpMng.agents[rid] = nil
			LongTcpMng.agentnum = LongTcpMng.agentnum - 1
		end
	end
	close_socket(fd)
end

function LongTcpMng.open_socket(fd, ipport)
	local status, result = skynet.pcall(skynet.call, service.gate_service, "lua", "accept", fd)

	if not status then
		skynet.pcall(skynet.call, service.gate_service, "lua", "kick", fd)
		return
	end

	LongTcpMng.connections[fd] = {
		fd = fd,
		ipport = ipport,
		time = timetool.get_time(),
		isclose = nil,
		rid = nil,
	}

	LongTcpMng.connectnum = LongTcpMng.connectnum + 1
	filelog.sys_info(filename .. " line144 New client from : " ..ipport)
end

-- socket 关闭的入口，先去agent:discount ,设置玩家掉线， fd=nil
function LongTcpMng.close_socket(fd)
	filelog.sys_info(filename .. " line158 LongTcpMng.close_socket, fd =" ..  fd)
	--如果对应的服务存在通知服务玩家掉线
	local c = LongTcpMng.connections[fd]
	local a
	if c ~= nil and c.rid ~= nil then
		a = LongTcpMng.agents[c.rid]
		if a ~= nil and a.fd == fd and a.agent ~= nil then
			if not c.isclose then
				skynet.pcall(skynet.send, a.agent, "lua", "cmd", "disconnect", fd)
			end
			return
		end
	end 
	close_socket(fd)
end

function LongTcpMng.create_session(fd, msgname, msgbody, realip)
 	local result = false
	local status = false
	if msgbody.rid == nil then
		return false
	end
	if not LongTcpMng.connections[fd] then
		return false
	end
	if (realip ~= nil) then
		LongTcpMng.connections[fd].ipport = realip .. ":0"
	end
	local rid = msgbody.rid
	--agent 还在节点中， 需要走断线重连过程
	if LongTcpMng.agents[rid] ~= nil 
		and LongTcpMng.agents[rid].agent ~= nil then
		local tmp_fd = LongTcpMng.agents[rid].fd
        local c = LongTcpMng.connections[fd]
        --连接对象为空，怎么处理
        if not c then
            return false
        end			
		--通知agent 更新fd		
		status, result = skynet.pcall(skynet.call, LongTcpMng.agents[rid].agent,
			 "lua", "cmd", "reconnect",
				{ipport=c.ipport, gate = service.gate_service, client = fd, watchdog = skynet.self(), msg=msgbody})
		--filelog.sys_info("line183 LongTcpMng.create_session reconnect")
		if not status then
			filelog.sys_warning("line185 LongTcpMng.create_session agent had exit", result)
			close_socket(fd)

			--通知释放之前的socket
			LongTcpMng.agents[rid] = nil			
			close_socket(tmp_fd)
			return false
		end

		if not result then
			filelog.sys_warning("line195 LongTcpMng.create_session agent had exit", result)
			close_socket(fd)
			return false			
		end

		if LongTcpMng.connections[fd] == nil then
			--filelog.sys_warning("line201 LongTcpMng.create_session reconn connections[fd] == nil", fd)
			--return false
			LongTcpMng.connections[fd] = {
				fd = fd,
				ipport = "",
				time = timetool.get_time(),
				isclose = nil,
				rid = nil,
			}
		end 
		--找到一个有效的agent
		LongTcpMng.agents[rid].fd = fd
		LongTcpMng.connections[fd].rid = rid
		LongTcpMng.connections[fd].isclose = false

		--通知释放之前的socket
		--filelog.sys_info("line217 LongTcpMng.create_session close_socket tmp_fd:", tmp_fd)
		close_socket(tmp_fd)
		return true
	elseif LongTcpMng.agents[rid] ~= nil and LongTcpMng.agents[rid].agent == nil then 
		if LongTcpMng.agents[rid].fd ~= nil then
			filelog.sys_info(filename .. "line222 LongTcpMng.create_session close_socket LongTcpMng.agents[rid].fd:", LongTcpMng.agents[rid].fd, rid)			
			close_socket(LongTcpMng.agents[rid].fd)
		end
	end

	local agentservice = LongTcpMng.agentpool:create_service()
	if agentservice == nil then
		filelog.sys_error("line229 LongTcpMng.create_session not enough agentservice")		
		return false
	end

	LongTcpMng.agents[rid] = {
		agent = agentservice.service,
		fd = fd
	}
	-- LongTcpMng.agents[rid].serviceid = agentservice.id
	if LongTcpMng.connections[fd] == nil then
		LongTcpMng.connections[fd] = {
			fd = fd,
			ipport = "",
			time = timetool.get_time(),
			isclose = nil,
			rid = nil,
		}
	end
	LongTcpMng.connections[fd].rid = rid
	LongTcpMng.connections[fd].isclose = false
	
	status, result = skynet.pcall(skynet.call, agentservice.service, "lua", "cmd", "create_session", {ipport=LongTcpMng.connections[fd].ipport, gate = service.gate_service, client = fd, watchdog = skynet.self(), msgname = msgname, msgbody=msgbody})
	if not status then
		filelog.sys_error("line252 LongTcpMng.create_session agent create_session failed", result)
		if LongTcpMng.agents[rid] ~= nil 
			and LongTcpMng.agents[rid].agent == agentservice.service
			and LongTcpMng.agents[rid].fd == fd then
			skynet.pcall(skynet.kill, LongTcpMng.agents[rid].agent)
			LongTcpMng.agents[rid] = nil
		end
		close_socket(fd)
		return false		
	end

	if not result then
		filelog.sys_error("line264 LongTcpMng.create_session agent create_session failed", result)
		if LongTcpMng.agents[rid] ~= nil 
			and LongTcpMng.agents[rid].agent == agentservice.service
			and LongTcpMng.agents[rid].fd == fd then
			skynet.pcall(skynet.kill, LongTcpMng.agents[rid].agent)
			LongTcpMng.agents[rid] = nil
		end
		close_socket(fd)
		return false		
	end
	if LongTcpMng.agents[rid] == nil or LongTcpMng.agents[rid].fd == nil then
		close_socket(fd)
		return false
	end

	if LongTcpMng.agents[rid].fd ~= fd then
		close_socket(fd)
		return false		
	end

	if LongTcpMng.connections[fd] == nil then
		LongTcpMng.connections[fd] = {
			fd = fd,
			ipport = "",
			time = timetool.get_time(),
			isclose = nil,
			rid = nil,
		}
		LongTcpMng.connections[fd].rid = rid
		LongTcpMng.connections[fd].isclose = false
	end
	LongTcpMng.agentnum = LongTcpMng.agentnum + 1
	return result
end

function LongTcpMng.get_agent_session(rid)
	return LongTcpMng.agents[rid]
end

function LongTcpMng.get_all_agents()
	return LongTcpMng.agents
end

function LongTcpMng.heart_timeout(fd)
	filelog.sys_info(filename .. " line310 LongTcpMng.heart_timeout", fd)
	local c = LongTcpMng.connections[fd]
	if c ~= nil then
		skynet.pcall(skynet.call, service.gate_service, "lua", "kick", fd)
		LongTcpMng.connections[fd].isclose = true
	end	
end

function LongTcpMng.clear()
	for fd, _ in pairs(LongTcpMng.connections) do
		 close_agent(fd)
	end

	if LongTcpMng.agentpool ~= nil then
		local iter = LongTcpMng.agentpool:idle_service_iter()
		local service = iter()
		while service do
			skynet.pcall(skynet.send, service, "lua", "cmd", "close")
			service = iter()
		end
	end
end 

return LongTcpMng